package solver

func (g *Grid) wingslist(pivot *Cell, wingCount int) [][]*Cell {
	cells := []*Cell{}
	wingslist := [][]*Cell{}
	for _, cell := range g.seenCells(pivot) {
		if equalCells(cell, pivot) || cell.candCount < 2 || cell.candCount > wingCount {
			continue
		}
		cells = append(cells, cell)
	}
	nums := make([]int, len(cells))
	for i := 0; i < len(cells); i++ {
		nums[i] = i
	}
	for _, index3 := range combine(nums, wingCount) {
		wings := make([]*Cell, wingCount)
		for i := 0; i < wingCount; i++ {
			wings[i] = cells[index3[i]]
		}
		wingslist = append(wingslist, wings)
	}
	return wingslist
}

func sameHouse(x *Cell, y *Cell) bool {
	return x.rowNum == y.rowNum || x.colNum == y.colNum || x.boxNum == y.boxNum
}

func (g *Grid) handleWXYZWing(pivot *Cell, wings []*Cell) (used bool) {
	if wings[0].candCount+wings[1].candCount+wings[2].candCount > 7 {
		return false
	}
	if wings[0].rowNum == wings[1].rowNum && wings[1].rowNum == wings[2].rowNum {
		return false
	}
	if wings[0].colNum == wings[1].colNum && wings[1].colNum == wings[2].colNum {
		return false
	}
	if wings[0].boxNum == wings[1].boxNum && wings[1].boxNum == wings[2].boxNum {
		return false
	}
	if wings[0].cands == wings[1].cands || wings[1].cands == wings[2].cands || wings[2].cands == wings[0].cands {
		return false
	}
	if wings[0].candCount+wings[1].candCount+wings[2].candCount == 7 {
		if wings[0].candCount == 3 {
			if !sameHouse(wings[0], wings[1]) && !sameHouse(wings[0], wings[2]) {
				return false
			}
		}
		if wings[1].candCount == 3 {
			if !sameHouse(wings[1], wings[0]) && !sameHouse(wings[1], wings[2]) {
				return false
			}
		}
		if wings[2].candCount == 3 {
			if !sameHouse(wings[2], wings[0]) && !sameHouse(wings[2], wings[1]) {
				return false
			}
		}
	}

	zbool := candAnd(candAnd(wings[0].cands, wings[1].cands), wings[2].cands)
	zs := intsFromBools(zbool)
	if len(zs) != 1 {
		return false
	}
	z := zs[0]

	wxyzbool := candOr(candOr(wings[0].cands, wings[1].cands), wings[2].cands)
	if pivot.candCount == 3 {
		wxyzbool = candAnd(wxyzbool, candNot(zbool))
	}
	if pivot.cands != wxyzbool {
		return false
	}
	//fmt.Println(pivot.rowNum, pivot.colNum, candsFromBool(pivot.candidates), candsFromBool(wxyzbool))
	//for _, wing := range wings {
	//fmt.Println(wing.rowNum, wing.colNum, candsFromBool(wing.candidates))

	//}

	return g.clearupWXYZWing(z, pivot, wings)
}

func (g *Grid) clearupWXYZWing(z int, pivot *Cell, wings []*Cell) (used bool) {
	cells := []*Cell{}
	if pivot.candCount == 4 {
		cells = g.seenCells(pivot)
	}
	for _, wing := range wings {
		cells = shareCells(cells, g.seenCells(wing))
	}
	for _, cell := range cells {
		if equalCells(cell, pivot) || containCell(wings, cell) {
			continue
		}
		if cell.removeCand(z) {
			used = true
		}
	}

	return
}

func (g *Grid) wXYZWingCell(pivot *Cell) (used bool) {
	for _, threeWings := range g.wingslist(pivot, 3) {
		if g.handleWXYZWing(pivot, threeWings) {
			used = true
		}
	}
	return
}

func (g *Grid) WXYWing() (used bool) {
	for _, row := range g.rows {
		for _, cell := range row.cells {
			if cell.candCount != 3 {
				continue
			}
			if g.wXYZWingCell(cell) {
				used = true
			}
		}
	}
	return
}

func (g *Grid) WXYZWing() (used bool) {
	for _, row := range g.rows {
		for _, cell := range row.cells {
			if cell.candCount != 4 {
				continue
			}
			if g.wXYZWingCell(cell) {
				used = true
			}
		}
	}
	return
}

